import { Injectable, OnModuleInit, Logger } from '@nestjs/common'
import { ModuleRef } from '@nestjs/core'
import { PluginModule } from '../interfaces/plugin.interface'
import * as fs from 'fs'
import * as path from 'path'
import { EventEmitter2 } from '@nestjs/event-emitter'
import * as unzipper from 'unzipper'
import * as chokidar from 'chokidar'
import * as jwt from 'jsonwebtoken'

/**
 * 插件管理服务，负责插件的加载、注册、启用、禁用、卸载、热重载等
 */
@Injectable()
export class PluginManagerService implements OnModuleInit {
  private readonly logger = new Logger('PluginManager')
  private plugins: Map<string, PluginModule> = new Map()
  private pluginStatus: Map<string, boolean> = new Map() // true=enabled

  private watcher: chokidar.FSWatcher

  constructor(
    private moduleRef: ModuleRef,
    private eventEmitter: EventEmitter2,
  ) {}

  /**
   * 应用启动时自动加载插件
   */
  async onModuleInit() {
    await this.loadPlugins()
    this.startHotReload()
  }

  /**
   * 加载 plugins 目录下所有插件
   */
  private async loadPlugins() {
    const pluginsDir = path.join(__dirname, '../../../plugins')
    if (!fs.existsSync(pluginsDir)) {
      fs.mkdirSync(pluginsDir, { recursive: true })
      return
    }
    const pluginFolders = fs
      .readdirSync(pluginsDir)
      .filter((folder) => fs.statSync(path.join(pluginsDir, folder)).isDirectory())
    for (const folder of pluginFolders) {
      try {
        const pluginPath = path.join(pluginsDir, folder)
        const pluginConfigPath = path.join(pluginPath, 'plugin.config.json')
        if (!fs.existsSync(pluginConfigPath)) continue
        const config = JSON.parse(fs.readFileSync(pluginConfigPath, 'utf8'))
        const mainFilePath = path.join(pluginPath, config.main)
        // 动态导入插件模块
        const pluginModule = await import(mainFilePath)
        const pluginClass = pluginModule.default || pluginModule[config.entry]
        if (!pluginClass) throw new Error(`Plugin entry not found in ${folder}`)
        // 创建插件实例
        const pluginInstance = await this.moduleRef.create(pluginClass)
        this.plugins.set(config.name, pluginInstance)
        this.pluginStatus.set(config.name, config.enabled || false)
        // 注册钩子
        if (pluginInstance.onRegister) await pluginInstance.onRegister()
        // 启用钩子
        if (this.isEnabled(config.name)) await this.enablePlugin(config.name)
        this.eventEmitter.emit('plugin.loaded', { name: config.name, version: config.version })
      } catch (error) {
        this.logger.error(`Failed to load plugin ${folder}: ${error.message}`)
        this.eventEmitter.emit('plugin.error', { plugin: folder, error: error.message })
      }
    }
  }

  /**
   * 自动解压插件包并注册
   * @param zipPath 插件zip包路径
   */
  async extractAndRegister(zipPath: string) {
    const pluginsDir = path.join(__dirname, '../../../plugins')
    // 1. 解压到 plugins/xxx 目录
    const zipName = path.basename(zipPath, '.zip')
    const destDir = path.join(pluginsDir, zipName)
    await fs
      .createReadStream(zipPath)
      .pipe(unzipper.Extract({ path: destDir }))
      .promise()

    // 2. 校验 plugin.config.json
    const configPath = path.join(destDir, 'plugin.config.json')
    if (!fs.existsSync(configPath)) {
      throw new Error('plugin.config.json 缺失')
    }
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))

    // 3. 注册插件
    await this.registerPlugin(destDir)

    this.logger.log(`插件 ${config.name} 解压并注册成功`)
    return config
  }

  /**
   * 注册插件（加载、调用onRegister等）
   */
  async registerPlugin(pluginDir: string) {
    // 1. 读取配置
    const configPath = path.join(pluginDir, 'plugin.config.json')
    const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
    const mainFilePath = path.join(pluginDir, config.main)

    // 2. 动态导入
    const pluginModule = await import(mainFilePath)
    const pluginClass = pluginModule.default || pluginModule[config.entry]
    if (!pluginClass) throw new Error(`Plugin entry not found in ${pluginDir}`)

    // 3. 创建实例并注册
    const pluginInstance = await this.moduleRef.create(pluginClass)
    this.plugins.set(config.name, pluginInstance)
    this.pluginStatus.set(config.name, config.enabled || false)

    if (pluginInstance.onRegister) await pluginInstance.onRegister()
    if (this.isEnabled(config.name)) await this.enablePlugin(config.name)

    this.eventEmitter.emit('plugin.loaded', { name: config.name, version: config.version })
  }

  /**
   * 启用插件
   */
  async enablePlugin(name: string) {
    if (!this.plugins.has(name)) throw new Error(`Plugin ${name} not found`)
    const plugin = this.plugins.get(name)
    this.pluginStatus.set(name, true)
    if (plugin?.onEnable) await plugin.onEnable()
    this.eventEmitter.emit('plugin.enabled', name)
  }

  /**
   * 禁用插件
   */
  async disablePlugin(name: string) {
    if (!this.plugins.has(name)) throw new Error(`Plugin ${name} not found`)
    const plugin = this.plugins.get(name)
    this.pluginStatus.set(name, false)
    if (plugin?.onDisable) await plugin.onDisable()
    this.eventEmitter.emit('plugin.disabled', name)
  }

  /**
   * 卸载插件
   */
  async uninstallPlugin(name: string) {
    if (!this.plugins.has(name)) throw new Error(`Plugin ${name} not found`)
    if (this.isEnabled(name)) await this.disablePlugin(name)
    const plugin = this.plugins.get(name)
    if (plugin?.onUninstall) await plugin.onUninstall()
    this.plugins.delete(name)
    this.pluginStatus.delete(name)
    // 删除插件目录
    const pluginDir = path.join(__dirname, '../../../plugins', name)
    fs.rmdirSync(pluginDir, { recursive: true })
    this.eventEmitter.emit('plugin.uninstalled', name)
  }

  /**
   * 插件热重载（开发环境）
   */
  async reloadPlugin(name: string) {
    await this.disablePlugin(name)
    await this.enablePlugin(name)
  }

  /**
   * 开发环境下自动热重载插件
   */
  startHotReload() {
    if (process.env.NODE_ENV !== 'development') return
    const pluginsDir = path.join(__dirname, '../../../plugins')
    this.watcher = chokidar.watch(pluginsDir, {
      ignored: /(^|[\/\\])\../,
      persistent: true,
      ignoreInitial: true,
    })
    this.watcher.on('change', async (filePath) => {
      const pluginName = path.basename(path.dirname(filePath))
      if (this.isEnabled(pluginName)) {
        this.logger.log(`热重载插件: ${pluginName}`)
        await this.disablePlugin(pluginName)
        await this.enablePlugin(pluginName)
      }
    })
  }

  /**
   * 检查插件是否启用
   */
  isEnabled(name: string): boolean {
    return this.pluginStatus.get(name) || false
  }

  /**
   * 获取所有插件信息
   */
  getAllPlugins() {
    return Array.from(this.plugins.values()).map((plugin) => ({
      name: plugin.name,
      version: plugin.version,
      description: plugin.description,
      enabled: this.isEnabled(plugin.name),
    }))
  }

  /**
   * 获取指定插件实例
   */
  getPlugin<T extends PluginModule>(name: string): T | undefined {
    return this.plugins.get(name) as T
  }

  /**
   * 校验市场签名
   */
  verifyMarketToken(token: string) {
    const secret = process.env.MARKETPLACE_PUBLIC_KEY // 或市场方提供的公钥
    if (!secret) {
      throw new Error('未配置插件市场公钥')
    }
    try {
      const payload = jwt.verify(token, secret as jwt.Secret)
      return payload
    } catch (e) {
      throw new Error('插件市场签名校验失败')
    }
  }
}
